#include "UltimateAsioNetworkPluginPrecompiled.h"
#include "UltimateAsioNetworkSubsystem.h"



UltimateAsioNetworkSubsystem::UltimateAsioNetworkSubsystem()
{
	disconnect=false;

}

UltimateAsioNetworkSubsystem::~UltimateAsioNetworkSubsystem()
{

}
void UltimateAsioNetworkSubsystem::accept(int port)
{
	bool placeFound = false;
	for(std::map<int,EntityNetworkRecord>::iterator i=entityRecordMap.begin(); i!=entityRecordMap.end(); i++)
	{
		if((!placeFound && (*i).second.entitySpotStatus==FREE)||(*i).second.entitySpotStatus==SERVER)
		{
			(*i).second.entityNetworkStatus=CONNECTED;
			if((*i).second.entitySpotStatus==FREE)
			{
				placeFound = true;
			}
			(*i).second.entitySpotStatus=OWN;
		}
	}

	boost::thread workerThread(&UltimateAsioNetworkSubsystem::sendAndReceive,this);

	try
	{
		boost::asio::io_service io_service;

		tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), port));


		for (int i=0;!disconnect;i++)
		{
			tcp::socket *socket = new tcp::socket(io_service);
			acceptor.accept(*socket);
			


			int freeId;
			for(std::map<int,EntityNetworkRecord>::iterator j=entityRecordMap.begin(); j!=entityRecordMap.end(); j++)
			{
				if( (*j).second.entitySpotStatus==FREE)
				{
					freeId = (*j).second.id;
					(*j).second.entitySpotStatus=SERVER_OTHERS;
					break;
				}
			}


			boost::system::error_code ignored_error;
			boost::asio::write(*socket, boost::asio::buffer(Ogre::StringConverter::toString(freeId)), ignored_error);
			//socket->write_some(boost::asio::buffer(Ogre::StringConverter::toString(freeId)), ignored_error);
			sockets.push_back( socket);
		}
	}
	catch (std::exception& e)
	{
		std::cerr << e.what() << std::endl;
	}


}


void UltimateAsioNetworkSubsystem::connect(std::string ip, int port)
{
	boost::asio::io_service io_service;

	tcp::resolver resolver(io_service);
	tcp::resolver::query query(ip,"daytime");
	tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);


	tcp::socket *socket = new tcp::socket(io_service);
	tcp::endpoint connectionEndpoint(endpoint_iterator->endpoint().address(), port);
	boost::system::error_code ec;
	socket->connect(connectionEndpoint, ec);
	sockets.push_back(socket);

	boost::array<char, 8> buf;
	boost::system::error_code error;
	size_t len = socket->read_some(boost::asio::buffer(buf), error);

	int index = buf.data()[0]-'0';

	
	entityRecordMap[index].entitySpotStatus=OWN;
	entityRecordMap[index].entityNetworkStatus=CONNECTED;

	for(std::map<int,EntityNetworkRecord>::iterator i=entityRecordMap.begin(); i!=entityRecordMap.end(); i++)
	{
		if((*i).second.entitySpotStatus!=OWN)
		{
			(*i).second.entitySpotStatus=OTHERS;
		}
	}


	boost::thread workerThread(&UltimateAsioNetworkSubsystem::sendAndReceive,this);

	workerThread.join();

}


void UltimateAsioNetworkSubsystem::listenOnPort(int port)
{
	boost::thread workerThread(&UltimateAsioNetworkSubsystem::accept,this,port);
}

void UltimateAsioNetworkSubsystem::connectToServer(std::string ip, int port)
{
	
	boost::thread workerThread(&UltimateAsioNetworkSubsystem::connect,this,ip,port);
}

UltimateNetworkSubsystem::EntityNetworkRecord UltimateAsioNetworkSubsystem::getRecordForPlayer(int id)
{

	return entityRecordMap[id];
}

void UltimateAsioNetworkSubsystem::setRecordForPlayer(int id, EntityNetworkRecord record)
{
	entityRecordMap[id] = record;

}

void UltimateAsioNetworkSubsystem::createRecordForPlayer(int id, bool server)
{
	EntityNetworkRecord newRecord;
	newRecord.id=id;
	if(server)
	{
		newRecord.entitySpotStatus= SERVER;
	}
	else
	{
		newRecord.entitySpotStatus= FREE;
	}

	newRecord.entityNetworkStatus =ALONE;

	entityRecordMap[id]= newRecord;
}


void UltimateAsioNetworkSubsystem::sendAndReceive()
{
	while(!disconnect)
	{

		for(std::map<int,EntityNetworkRecord>::iterator i=entityRecordMap.begin(); i!=entityRecordMap.end(); i++)
		{
			if((*i).second.entitySpotStatus==OWN || (*i).second.entitySpotStatus==SERVER_OTHERS )
			{

				for(int j=0; j<sockets.size();j++)
				{

					tcp::socket* socket = sockets[j];

					int sendingNetworkState=0;
					switch((*i).second.entityNetworkStatus)
					{
					case CONNECTED: sendingNetworkState=0;
						break;
					case PLAYING: sendingNetworkState=1;
						break;
					case DISCONNECTED: sendingNetworkState=2;
						break;
					}


					char buffer [128];
					int n=sprintf (buffer,"%d%012d%012d%012d%1.10f%1.10f%1.10f%1.10f%d", 
						((int)(*i).second.id),
						((int)(*i).second.position.x), ((int)(*i).second.position.y),((int) (*i).second.position.z),
						((*i).second.orientation.x), ((*i).second.orientation.y),((*i).second.orientation.z),((*i).second.orientation.w),
						sendingNetworkState);

					boost::system::error_code ignored_error;
					boost::asio::write(*socket, boost::asio::buffer(buffer), ignored_error);
					//sockets[i]->write_some( boost::asio::buffer(""), ignored_error);

					boost::array<char, 128> buf;
					boost::system::error_code error;
					size_t len = socket->read_some(boost::asio::buffer(buf), error);

					std::string result(buf.data());
					int id =Ogre::StringConverter::parseInt(result.substr(0,1));
					int posx = Ogre::StringConverter::parseInt(result.substr(1,12));
					int posy = Ogre::StringConverter::parseInt(result.substr(13,12));
					int posz = Ogre::StringConverter::parseInt(result.substr(25,12));
					float orix = Ogre::StringConverter::parseReal(result.substr(37,12));
					float oriy = Ogre::StringConverter::parseReal(result.substr(49,12));
					float oriz = Ogre::StringConverter::parseReal(result.substr(61,12));
					float oriw = Ogre::StringConverter::parseReal(result.substr(73,12));
					int networkState = Ogre::StringConverter::parseInt( result.substr(85,1));

					entityRecordMap[id].position.x= posx;
					entityRecordMap[id].position.y= posy;
					entityRecordMap[id].position.z= posz;
					entityRecordMap[id].orientation.x= orix;
					entityRecordMap[id].orientation.y= oriy;
					entityRecordMap[id].orientation.z= oriz;
					entityRecordMap[id].orientation.w= oriw;
					switch(networkState)
					{
					case 0: entityRecordMap[id].entityNetworkStatus=CONNECTED;
						break;
					case 1: entityRecordMap[id].entityNetworkStatus=PLAYING;
						break;
					case 2: entityRecordMap[id].entityNetworkStatus=DISCONNECTED;
						break;
					}

					

				}


			}
		}






	}
}


void UltimateAsioNetworkSubsystem::destroyConnection()
{
	disconnect=true;

}


